﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Collections.ObjectModel;

namespace Calhoon.MarkupParser
{
	public class HierarchyNode : Tag
	{
		protected virtual string OpeningSequence {
			get {
				return "<";
			}
		}

		protected virtual string ClosingSequence {
			get {
				return ">";
			}
		}

		private Collection<HierarchyNode> mChildren;
		public Collection<HierarchyNode> Children {
			get {
				return mChildren;
			}
		}

		public HierarchyNode() {
			mChildren = new Collection<HierarchyNode>();
		}

		public HierarchyNode(Tag createFrom)
			: this() {
			this.TagName = createFrom.TagName;

			foreach (KeyValuePair<string, AttributeValue> attribute in createFrom.Attributes) {
				this.Attributes.Add(attribute.Key, attribute.Value);
			}
		}

		public string Render() {
			StringBuilder output = new StringBuilder();
			Render(0, output);

			return output.ToString();
		}

		protected virtual void Render(int level, StringBuilder input) {
			RenderOpeningTag(level, input);
			RenderChildren(level, input);
			RenderClosingTag(level, input);
		}

		protected virtual void RenderOpeningTag(int level, StringBuilder input) {
			if (mChildren.Count > 0 && IsNewLine(input)) {
				input.Append('\n');
				Indent(level, input);
			}

			input.Append(OpeningSequence);
			input.Append(TagName);
			input.Append(RenderAttributes());

			if(mChildren.Count == 0) {
				input.Append('/');
			}

			input.Append(ClosingSequence);

			if (mChildren.Count > 0 && IsNewLine(input)) {
				input.Append('\n');
			}
		}

		protected virtual void RenderClosingTag(int level, StringBuilder input) {
			if (mChildren.Count > 0 && IsNewLine(input)) {
				input.Append('\n');
				Indent(level, input);
				input.Append(OpeningSequence);
				input.Append('/');
				input.Append(TagName);
				input.Append(ClosingSequence);
				input.Append('\n');
			}
		}

		protected virtual void RenderChildren(int level, StringBuilder input) {
			foreach (HierarchyNode child in mChildren) {
				checked {
					child.Render(level + 1, input);
				}
			}
		}

		protected static void Indent(int level, StringBuilder input) {
			for (int i = 0; i < level; ++i) {
				input.Append('\t');
			}
		}

		protected static bool IsNewLine(StringBuilder input) {
			return input.Length == 0 || input[input.Length - 1] != '\n';
		}

		protected static string GetIndentation(int level) {
			StringBuilder indent = new StringBuilder();
			for (int i = 0; i < level; ++i) {
				indent.Append('\t');
			}

			return indent.ToString();
		}

		public override bool Equals(object obj) {
			HierarchyNode otherNode = obj as HierarchyNode;
			if (obj == null) {
				return false;
			}

			if (this.Children.Count != otherNode.Children.Count) {
				return false;
			}

			for (int i = 0; i < mChildren.Count; ++i) {
				if (!this.mChildren[i].Equals(otherNode.mChildren[i])) {
					return false;
				}
			}

			return true;
		}

		public override int GetHashCode() {
			return base.GetHashCode() ^ mChildren.GetHashCode();
		}
	}
}